package JGameAdapter;

import ADT.Values;
import ADT.Length;
import ADT.Mass;
import ADT.Power;
import ADT.Speed;
import ADT.TimeDiff;
import static ADT.Values.*;
import ADT.Acc;
import ADT.Force;
public class FakeParticle implements ParticleInterface{

 /** ****************************************************
 *  ToDo: Moin! ich habe die Bremsen falsch gedacht und habe versucht sie umzuschreiben, aber die funktion forceBrake ist fehlerhaft.
 *  Lasse mir die Bremsen morgen von Jan und tim erklären und dann werden wir sehen, wie das ABS und ASR funzt.
 *  des weiteren müssen wir die RangeException wohl rausbekommen, weil wir sonst im JGame Methoden überschreiben würden,
 *  die nicht überschreibbar sind. Dann müssen wir noch die getter und setter implementieren, aber auch das wird fix gemacht sein. sorry dass ich 
 *  nicht weiter gekommen bin
 *  
 * 
 *  Es ist jetzt 20 vor 3. ich gehe jetzt schlafen. cheerio
 * 
 *  PS: JGame ist als Library drin, hoffe das funzt bei dir auch so.
 * 
 * *****************************************************
 */




    // Antriebshebel
    private double level;
    // Zeit in S
    private TimeDiff time;
    // Position
    private Length pos;
    // Geschwindigtkeit in m/s
    private Speed speed;
    // Bremse
    private double brake;
    
    //Traktionskoeffizient
    private double traction;
    
    // Gewicht in KG
    private static Mass Mass = massInKg(1445.0);
    // Max. Leistung in KW
    private static Power PowerPropMax = powerInW(456000.0);
    // Max. Speed in m/s
    private static Speed SpeedMax = speedInMs(91.6);
    // Min. Speed in m/s
    private static Speed SpeedMin = speedInMs(0.0);
    // Erdbeschleunigung in m/(s*s)
    private static Acc AccEarth = Values.AccEarth;
    // max. Kraft
    private Force ForcePropMax = Mass.mul(AccEarth);
    
    // aktuelle Kraft
    private Force ForcePropMaxCurrent = ForcePropMax;
    
    // max. DragConst
    private static Force DragConst = forceInN(Math.abs(PowerPropMax.getW() / (Math.pow(SpeedMax.getMS(), 3))));
    private static double MinLevel = 0.0;
    private static double MaxLevel = 1.0;
    // Umrechnug von m/s nach km/h
    private static double METERPERSECONDSTOKILOMETERPERHOUER = 3.6;

    private boolean abs;
    private boolean asr;
    private boolean abflug;
    private boolean asrActive;
    private boolean absActive;
    
    
    // Der Konstruktor
    public FakeParticle() //throws RangeException
    {  // Variablen auf Default-Werte setzen
        reset();
        
    }
    private boolean checkLevelRange(double Level) //throws RangeException
    {
        // Rückgabe
        boolean bReturn = true;

        // Eingabe im gültigen Bereich?
        if (!((Level <= MaxLevel) && (Level >= MinLevel))) {
            // Nicht aktzeptiert
            bReturn = false;
        }

        // und ab damit
        return bReturn;
    }

    // Setzt alle wichtigen Variablen
    public void set(double Time, double Pos, double Speed, double Level, double Brake) //throws RangeException
    {
        if (checkLevelRange(Level)) {
            this.time = timeDiffInS(Time);
            this.pos = lengthInM(Pos);
            this.speed = speedInMs(Speed);
            this.level = Level;
            this.brake = Brake;
        } else {
            System.out.println("Impossible level");
        }
    }

    // Setzt alle wichtigen Variablen auf Default-Werte zurück
    public void reset() //throws RangeException
    {
        set(0.0, 0.0, 0.0, 0.0, 0.0);
        abs = true;
        asr = true;
        abflug = false;
    }

//    // Einen Simulations-Schritt ausführen, wenn nur ein level gegeben wird
    public void step(TimeDiff DeltaTime, double Level, double BrakeLevel) //throws RangeException
    {
        // Manipulation nur durchführen, wenn Level gültig ist
        if( checkLevelRange(Level) && checkLevelRange(BrakeLevel) )
        {
            // Regler sind aus
            absActive = false;
            asrActive = false;
            
            // Wie viel % der Leistung
            Power powerProp = PowerPropMax.mul(Math.abs(Level));

            // Formeln anwenden
            Force forcePropAbs = forceInN(Math.min(ForcePropMax.n(),(powerProp.div(Math.max(0.0001, Math.abs(speed.getMS())))).getW()));
            Force forceProp = forcePropAbs.mul(Math.signum(Level));
            Force forceDrag = forceInN(DragConst.n() * (speed.getMS() * speed.getMS()) * Math.signum(-speed.getMS()));
            Force forceBrake = (((Mass.mul(AccEarth)).mul(BrakeLevel)).mul(Math.signum(-speed.getMS())));
            Force force = forceDrag;

            // ABS
            if(Math.abs(forceBrake.n()) > ForcePropMaxCurrent.n())
            {
                if(abs == false)
                {
                    abflug = true;
                }
                else
                {
                    absActive = true;
                    force = force.add(ForcePropMaxCurrent.mul(Math.signum(-speed.getMS())));
                }
            }
            else
                force = force.add(forceBrake);

            // ASR
            if(Math.abs(forceProp.n()) > ForcePropMaxCurrent.n())
            {
                if(asr == false)
                {
                    abflug = true;
                }
                else
                {
                    asrActive = true;
                    force = force.add(ForcePropMaxCurrent);
                }
            }
            else
                force = force.add(forceProp);

            // Beschleunigung setzen
            Acc acc = force.div(Mass);

            // Interne Werte setzen
            this.level = Level;
            this.brake = BrakeLevel;
           
            time = time.add(DeltaTime);
            pos = pos.add(speed.mul(DeltaTime));
            speed = speed.add(acc.mul(DeltaTime));
            System.out.println("deltatime" + DeltaTime.s());
            System.out.println("time" + time.s() );
        }
    }

    // Aktuelle Geschwindigkeit in m/s
    public double getSpeed() {
        return speed.getMS();
    }

    public void setSpeed(double Speed) {
        this.speed = speedInMs(Speed);

    }

    public double getSpeedInKmH() {
        return speed.getKmH();
    }

    // Aktuelle Position in Metern
    public double getPos() {
        return pos.getM();
    }

    public void setPos(double Pos) {
        this.pos = lengthInM(Pos);
    }
    // Aktuelle Simulationszeit in Sekunden

    public double getTime() {
        return time.s();
    }

    public void setTime(double Time) {
        this.time = timeDiffInS(Time);
    }

    public double getLevel() {return level;}

    //public void setLevel(double Level) {
    //    this.level = Level;
    //}

    public double getBrake() {return brake;}

    //public void setBrake(double Brake) {
    //    this.brake = Brake;
    //}
    
    public double getTraction() {return traction;}
    
    public void setTraction(double Traction) {
        this.traction = Traction;
        this.ForcePropMaxCurrent = ForcePropMax.mul(Traction);
    }
    
    public double getForcePropMax() {return ForcePropMax.n();}
    
    public void setForcePropMax(double forcePropMax){this.ForcePropMax = forceInN(forcePropMax);}
    
    public boolean getAbs(){return abs;}
    
    public void setAbs(boolean abs) {this.abs = abs;}
    
    public boolean getAsr() {return asr;}
    public void setAsr(boolean asr){this.asr = asr;}

    public boolean absActive() {return absActive;}

    public boolean asrActive(){return asrActive;}

    public boolean abflug(){return abflug;}
}

